package com.gitee.feizns.quickstart.ding.talk.api.v10.yida;

import com.aliyun.dingtalkyida_1_0.models.BatchGetFormDataByIdListRequest;
import com.aliyun.dingtalkyida_1_0.models.BatchGetFormDataByIdListResponseBody;
import com.aliyun.dingtalkyida_1_0.models.BatchRemovalByFormInstanceIdListRequest;
import com.aliyun.dingtalkyida_1_0.models.BatchSaveFormDataRequest;
import com.aliyun.dingtalkyida_1_0.models.BatchUpdateFormDataByInstanceMapRequest;
import com.aliyun.dingtalkyida_1_0.models.DeleteFormDataRequest;
import com.aliyun.dingtalkyida_1_0.models.GetFieldDefByUuidRequest;
import com.aliyun.dingtalkyida_1_0.models.GetFieldDefByUuidResponseBody;
import com.aliyun.dingtalkyida_1_0.models.GetFormDataByIDRequest;
import com.aliyun.dingtalkyida_1_0.models.GetFormDataByIDResponseBody;
import com.aliyun.dingtalkyida_1_0.models.ListTableDataByFormInstanceIdTableIdRequest;
import com.aliyun.dingtalkyida_1_0.models.SearchFormDataIdListRequest;
import com.aliyun.dingtalkyida_1_0.models.SearchFormDataSecondGenerationRequest;
import com.aliyun.dingtalkyida_1_0.models.SearchFormDataSecondGenerationResponseBody;
import com.aliyun.dingtalkyida_1_0.models.SearchFormDatasRequest;
import com.aliyun.dingtalkyida_1_0.models.SearchFormDatasResponseBody;
import com.aliyun.dingtalkyida_1_0.models.UpdateFormDataRequest;
import com.gitee.feizns.dynamic.Lists;
import com.gitee.feizns.quickstart.ding.talk.api.v10.oauth2.AccessToken;
import com.gitee.feizns.quickstart.ding.talk.api.v10.yida.forms.FormFields;
import com.gitee.feizns.quickstart.ding.talk.api.v10.yida.forms.InnerTables;
import com.gitee.feizns.quickstart.ding.talk.api.v10.yida.forms.instances.BatchRemove;
import com.gitee.feizns.quickstart.ding.talk.api.v10.yida.forms.instances.BatchSave;
import com.gitee.feizns.quickstart.ding.talk.api.v10.yida.forms.instances.Datas;
import com.gitee.feizns.quickstart.ding.talk.api.v10.yida.forms.instances.DeleteById;
import com.gitee.feizns.quickstart.ding.talk.api.v10.yida.forms.instances.GetById;
import com.gitee.feizns.quickstart.ding.talk.api.v10.yida.forms.instances.Ids;
import com.gitee.feizns.quickstart.ding.talk.api.v10.yida.forms.instances.IdsQuery;
import com.gitee.feizns.quickstart.ding.talk.api.v10.yida.forms.instances.InsertOrUpdate;
import com.gitee.feizns.quickstart.ding.talk.api.v10.yida.forms.instances.PutById;
import com.gitee.feizns.quickstart.ding.talk.api.v10.yida.forms.instances.Search;
import com.gitee.feizns.quickstart.ding.talk.api.v10.yida.forms.instances.advances.QueryAll;
import com.gitee.feizns.quickstart.ding.talk.domain.SearchCondition;
import com.gitee.feizns.quickstart.jackson.JsonUtils;
import lombok.SneakyThrows;

import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.stream.Collectors;

/**
 * 获取指定应用下的表单列表（/v1.0/yida/forms）
 * 接口路径：<a href="https://open.dingtalk.com/document/orgapp/depending-on-the-application-id-to-get-the-form-list">获取指定应用下的表单列表（/v1.0/yida/forms）</a>
 * @link <a href="https://open.dingtalk.com/document/orgapp/depending-on-the-application-id-to-get-the-form-list">服务端API 宜搭应用开发 表单 获取指定应用下的表单列表</a>
 * @author feizns
 * @since 2024/3/18
 */
public class FormsApi {

    /**
     * 表单
     */
    private final YiDaConfig yiDaConfig;

    /**
     * token
     */
    private final AccessToken accessToken;

    /**
     * 表单API
     * @param yiDaConfig Yi da config
     */
    public FormsApi(YiDaConfig yiDaConfig) {
        this(yiDaConfig, new AccessToken(yiDaConfig.getConfig()));
    }

    /**
     * 表单API
     * @param yiDaConfig Yi da config
     * @param accessToken 访问令牌
     */
    public FormsApi(YiDaConfig yiDaConfig, AccessToken accessToken) {
        this.yiDaConfig = yiDaConfig;
        this.accessToken = accessToken;
    }

    /**
     * 根据表单编号查询表单数据
     * @param id 编号
     */
    public GetFormDataByIDResponseBody getById(String id) {
        return GetById.execute(accessToken.get(), id, new GetFormDataByIDRequest(){{
            setSystemToken(yiDaConfig.getSystemToken());
            setLanguage(yiDaConfig.getLanguage());
            setAppType(yiDaConfig.getAppType());
            setUserId(yiDaConfig.getUserId());
        }});
    }

    /**
     * 根据表单编号删除表单数据
     * @param id 编号
     */
    public void deleteById(String id) {
        DeleteById.execute(accessToken.get(), new DeleteFormDataRequest(){{
            setSystemToken(yiDaConfig.getSystemToken());
            setLanguage(yiDaConfig.getLanguage());
            setAppType(yiDaConfig.getAppType());
            setUserId(yiDaConfig.getUserId());
            setFormInstanceId(id);
        }});
    }

    /**
     * 根据表单编号删除表单数据
     * @param formUuid1 表单 UUID
     * @param ids 编号列表
     */
    public void batchRemove(String formUuid1, String... ids) {
        BatchRemove.execute(accessToken.get(), new BatchRemovalByFormInstanceIdListRequest(){{
            setSystemToken(yiDaConfig.getSystemToken());
            setAppType(yiDaConfig.getAppType());
            setUserId(yiDaConfig.getUserId());
            setFormUuid(formUuid1);
            setFormInstanceIdList(Arrays.asList(ids));
            setAsynchronousExecution(false);
            setExecuteExpression(false);
        }});
    }

    /**
     * 全部删除
     * @param formUuid 表单 UUID
     */
    public void deleteAll(String formUuid) {
        while ( true ) {
            List<String> ids = ids(formUuid, 0, 100);
            if ( ids != null && !ids.isEmpty() ) {
                batchRemove(formUuid, ids.toArray(new String[]{}));
                System.out.println("删除成功：" + ids.size() + "条  :  " + ids);
            } else
                break;
        }
    }

    /**
     * 全部删除
     * @param formUuid 表单 UUID
     */
    public void deleteAll(String formUuid, SearchFormDataIdListRequest req) {
        while ( true ) {
            req.setPageNumber(0);
            req.setPageSize(100);
            List<String> ids = ids(formUuid, req);
            if ( ids != null && !ids.isEmpty() ) {
                Lists.partition(ids, 500).forEach(i -> {
                    batchRemove(formUuid, i.toArray(new String[]{}));
                    System.out.println("删除成功：" + i.size() + "条  :  " + i);
                });
            } else
                break;
        }
    }

    /**
     * 根据表单编号修改表单数据
     * @param id 编号
     */
    public void putById(String id, Map<String, Object> data) {
        putById(id, data, null);
    }

    /**
     * 根据表单编号修改表单数据
     * @param id 编号
     */
    public void putById(String id, Map<String, Object> data, Boolean isUseLatestVersion) {
        PutById.execute(accessToken.get(), new UpdateFormDataRequest(){{
            setSystemToken(yiDaConfig.getSystemToken());
            setLanguage(yiDaConfig.getLanguage());
            setAppType(yiDaConfig.getAppType());
            setUserId(yiDaConfig.getUserId());
            setFormInstanceId(id);
            setUseLatestVersion(isUseLatestVersion);
            setUpdateFormDataJson(JsonUtils.toJson(data));
        }});
    }

    /**
     * 子表单数据
     * @param formInstanceId 表单实例编号
     * @param formUuid 表单编号
     * @param tableFieldId 表单域编号
     * @return {@link List}<{@link Map}<{@link String}, {@link ?}>>
     */
    public List<Map<String, ?>> innerTables(String formInstanceId, String formUuid, String tableFieldId) {
        ListTableDataByFormInstanceIdTableIdRequest req = new ListTableDataByFormInstanceIdTableIdRequest();
        req.setFormUuid(formUuid);
        req.setAppType(yiDaConfig.getAppType());
        req.setSystemToken(yiDaConfig.getSystemToken());
        req.setUserId(yiDaConfig.getUserId());
        req.setTableFieldId(tableFieldId);
        return InnerTables.execute(accessToken.get(), formInstanceId, req);
    }

    /**
     * 获取表单内的组件信息
     * @param formUuid 表单编号
     * @return {@link List}<{@link GetFieldDefByUuidResponseBody.GetFieldDefByUuidResponseBodyResult}>
     */
    public List<GetFieldDefByUuidResponseBody.GetFieldDefByUuidResponseBodyResult> formFields(String formUuid) {
        GetFieldDefByUuidRequest req = new GetFieldDefByUuidRequest();
        req.setUserId(yiDaConfig.getUserId());
        req.setAppType(yiDaConfig.getAppType());
        req.setFormUuid(formUuid);
        req.setSystemToken(yiDaConfig.getSystemToken());
        return FormFields.execute(accessToken.get(), req);
    }

    /**
     * 获取表单内的组件信息
     * @param req {@link SearchFormDatasRequest}
     * @return {@link SearchFormDatasResponseBody}
     */
    public SearchFormDatasResponseBody search(SearchFormDatasRequest req) {
        req.setUserId(yiDaConfig.getUserId());
        req.setAppType(yiDaConfig.getAppType());
        req.setSystemToken(yiDaConfig.getSystemToken());
        req.setLanguage(yiDaConfig.getLanguage());
        return Search.execute(accessToken.get(), req);
    }

    /**
     * 所有编号列表
     * @param formUuid 表单编号
     * @param req {@link SearchFormDataIdListRequest}
     */
    public void ids(String formUuid, SearchFormDataIdListRequest req, Consumer<String> idConsumer) {
        req.setUserId(yiDaConfig.getUserId());
        req.setLanguage(yiDaConfig.getLanguage());
        req.setSystemToken(yiDaConfig.getSystemToken());
        Ids.executeAll(accessToken.get(), yiDaConfig.getAppType(), formUuid, req, idConsumer);
    }

    /**
     * 编号列表
     * @param formUuid 表单编号
     * @param req {@link SearchFormDataIdListRequest}
     * @return {@link List}<{@link String}>
     */
    public List<String> ids(String formUuid, SearchFormDataIdListRequest req) {
        req.setUserId(yiDaConfig.getUserId());
        req.setLanguage(yiDaConfig.getLanguage());
        req.setSystemToken(yiDaConfig.getSystemToken());
        return Ids.executeAll(accessToken.get(), yiDaConfig.getAppType(), formUuid, req);
    }

    /**
     * 编号列表
     * @param formUuid 表单编号
     * @param pageNo 分页参数不
     * @param pageSize 分页参数大小
     * @return {@link List}<{@link String}>
     */
    public List<String> ids(String formUuid, int pageNo, int pageSize) {
        SearchFormDataIdListRequest req = new SearchFormDataIdListRequest();
        req.setPageNumber(pageNo);
        req.setPageSize(pageSize);
        req.setSystemToken(yiDaConfig.getSystemToken());
        req.setUserId(yiDaConfig.getUserId());
        return Ids.execute(accessToken.get(), yiDaConfig.getAppType(), formUuid, req).getData();
    }

    /**
     * 编号列表查询
     * @param formUuid 表格 UUID
     * @param needFormInstanceValue 需要表单实例值
     * @param instanceIds 实例编号列表
     * @return {@link List}<{@link BatchGetFormDataByIdListResponseBody.BatchGetFormDataByIdListResponseBodyResult}>
     */
    @SneakyThrows
    public List<BatchGetFormDataByIdListResponseBody.BatchGetFormDataByIdListResponseBodyResult> idsQuery(String formUuid, boolean needFormInstanceValue, String... instanceIds) {
        BatchGetFormDataByIdListRequest req = new BatchGetFormDataByIdListRequest()
                .setFormUuid(formUuid)
                .setAppType(yiDaConfig.getAppType())
                .setSystemToken(yiDaConfig.getSystemToken())
                .setFormInstanceIdList(Arrays.asList(instanceIds))
                .setNeedFormInstanceValue(needFormInstanceValue)
                .setUserId(yiDaConfig.getUserId());
        return IdsQuery.execute(accessToken.get(), req);
    }

    /**
     * 编号列表查询
     * @param formUuid 表格 UUID
     * @param formDataJsonList 表单数据JSON列表
     * @return {@link List}<{@link String}>
     */
    @SneakyThrows
    public List<String> batchSave(String formUuid, List<String> formDataJsonList) {
        BatchSaveFormDataRequest req = new BatchSaveFormDataRequest();
        req.setFormUuid(formUuid);
        req.setAppType(yiDaConfig.getAppType());
        req.setSystemToken(yiDaConfig.getSystemToken());
        req.setUserId(yiDaConfig.getUserId());
        req.setFormDataJsonList(formDataJsonList);
        req.setNoExecuteExpression(true);
        req.setAsynchronousExecution(false);
        req.setKeepRunningAfterException(false);
        return BatchSave.execute(accessToken.get(), req);
    }

    /**
     * 批量修改
     * @param formUuid 表格 UUID
     * @param formDataJsonList 表单数据JSON列表
     * @return {@link List}<{@link String}>
     */
    @SneakyThrows
    public List<String> datas(String formUuid, Map<String, ?> formDataJsonList) {
        BatchUpdateFormDataByInstanceMapRequest req = new BatchUpdateFormDataByInstanceMapRequest();
        req.setFormUuid(formUuid);
        req.setAppType(yiDaConfig.getAppType());
        req.setSystemToken(yiDaConfig.getSystemToken());
        req.setUserId(yiDaConfig.getUserId());
        req.setUpdateFormDataJsonMap(formDataJsonList);
        return Datas.execute(accessToken.get(), req);
    }

    /**
     * 插入或更新
     * @param formUuid 表格 UUID
     * @param searchConditions 搜索条件
     * @param formDataJson 表单数据 JSON
     * @return {@link List}<{@link String}>
     */
    public List<String> insertOrUpdate(String formUuid, String formDataJson, SearchCondition... searchConditions) {
        return InsertOrUpdate.execute(accessToken.get(),
                yiDaConfig.getAppType(),
                yiDaConfig.getSystemToken(),
                formUuid,
                yiDaConfig.getUserId(),
                formDataJson,
                false,
                searchConditions);
    }

    /**
     * 获取表单内指定组件名称的表单项id列表
     * @param formUuid 表单编号
     * @return {@link List}<{@link GetFieldDefByUuidResponseBody.GetFieldDefByUuidResponseBodyResult}>
     */
    public List<String> formFieldIds(String formUuid, String componentName) {
        return formFields(formUuid)
                .stream()
                .filter(r -> r.getComponentName().equals(componentName))
                .map(GetFieldDefByUuidResponseBody.GetFieldDefByUuidResponseBodyResult::getFieldId)
                .collect(Collectors.toList());
    }

    /**
     * 根据编号获取表单数据（包含子表单数据）
     * @param formInstId 表单实例编号
     * @param fromUuid 表单编号
     * @return {@link GetFormDataByIDResponseBody}
     */
    public GetFormDataByIDResponseBody getById(String formInstId, String fromUuid) {
        GetFormDataByIDResponseBody body = getById(formInstId);
        if ( body == null || body.getFormInstId() == null )
            throw new RuntimeException(String.format("实例数据['%s']不存在.", formInstId));
        Map<String, Object> formData = new LinkedHashMap<>(body.getFormData());
        //获取所有子表单编号，并通过子表单数据接口获取子表单数据
        List<String> ids = formFieldIds(fromUuid, "TableField");
        for (String id : ids)
            formData.put(id, innerTables(formInstId, fromUuid, id));
        body.setFormData(formData);
        return body;
    }

    /**
     * 通过高级查询条件获取表单实例数据（包括子表单组件数据）
     * @param req {@link SearchFormDataSecondGenerationRequest}
     * @return {@link SearchFormDataSecondGenerationResponseBody}
     */
    @SneakyThrows
    public SearchFormDataSecondGenerationResponseBody queryAll(SearchFormDataSecondGenerationRequest req) {
        req.setAppType(yiDaConfig.getAppType());
        req.setSystemToken(yiDaConfig.getSystemToken());
        req.setUserId(yiDaConfig.getUserId());
        return QueryAll.execute(accessToken.get(), req);
    }

    /**
     * 通过高级查询条件获取表单实例数据（包括子表单组件数据）
     * @param formUuid 表单 UUID
     * @return {@link SearchFormDataSecondGenerationResponseBody}
     */
    @SneakyThrows
    public SearchFormDataSecondGenerationResponseBody queryAll(String formUuid, SearchCondition... conditions) {
        SearchFormDataSecondGenerationRequest req = new SearchFormDataSecondGenerationRequest();
        req.setFormUuid(formUuid);
        req.setSearchCondition(JsonUtils.toJson(Arrays.asList(conditions)));
        return queryAll(req);
    }

    /**
     * 通过高级查询条件获取表单实例数据（包括子表单组件数据）
     * @param formUuid 表单 UUID
     * @param conditions 条件
     * @return {@link SearchFormDataSecondGenerationResponseBody.SearchFormDataSecondGenerationResponseBodyData}
     */
    @SneakyThrows
    public SearchFormDataSecondGenerationResponseBody.SearchFormDataSecondGenerationResponseBodyData queryFirst(String formUuid, SearchCondition... conditions) {
        SearchFormDataSecondGenerationResponseBody body = queryAll(formUuid, conditions);
        return body != null && body.getData() != null && !body.getData().isEmpty() ? body.getData().get(0) : null;
    }

    /**
     * 通过高级查询条件获取表单实例数据（包括子表单组件数据）
     * @param formUuid 表单 UUID
     * @param conditions 条件
     * @return {@link SearchFormDataSecondGenerationResponseBody.SearchFormDataSecondGenerationResponseBodyData}
     */
    @SneakyThrows
    public String queryFirstFormInstanceId(String formUuid, SearchCondition... conditions) {
        SearchFormDataSecondGenerationResponseBody.SearchFormDataSecondGenerationResponseBodyData data = queryFirst(formUuid, conditions);
        return data != null ? data.getFormInstanceId() : null;
    }

}
